-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local interface_add = org_freedesktop_dbus.ObjMgrInterfacesAdded
local interface_del = org_freedesktop_dbus.ObjMgrInterfacesRemoved
local match_rule = org_freedesktop_dbus.MatchRule
local comm_defs = require 'device_mgmt.comm_defs'
local ctx = require 'mc.context'
local mdb = require 'mc.mdb'

local comm_fun = {}

-- 通用的获取所有设备路径函数
function comm_fun.get_all_device_paths(bus, path_pattern, depth, interface_name)
    return bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH, comm_defs.MDB_INTERFACE, 'GetSubPaths',
        'a{ss}sias', ctx.get_context_or_default(), path_pattern, depth, {interface_name})
end

-- 通用的获取父对象路径函数
function comm_fun.get_parent_path(bus, device_path)
    local ret = bus:call(comm_defs.MACA_SERVICE, comm_defs.MDB_PATH, comm_defs.MDB_INTERFACE, 'GetObject', 'a{ss}sas',
        ctx.get_context_or_default(), device_path, {comm_defs.OBJECT_PROPERTIES_INTERFACE})
    local device_obj
    for _, interfaces in pairs(ret) do
        for _, interface in pairs(interfaces) do
            mdb.register_interface(interface)
            device_obj = mdb.get_object(bus, device_path, interface)
            goto continue
        end
    end
    ::continue::
    if not device_obj then
        log:notice('get_parent_path failed, device_path is %s', device_path)
        return
    end
    local parent_path = comm_fun.get_prop(device_obj, 'ParentPath')
    return parent_path
end

-- 通用的设置信号监听函数
function comm_fun.set_interface_add_signal(bus, sig_slot, path_pattern, interface_name, cb)
    local interface_add_sig = match_rule.signal(interface_add.name, interface_add.interface):with_path_namespace(
        path_pattern)
    sig_slot[#sig_slot + 1] = bus:match(interface_add_sig, function(msg)
        local path, interfaces_and_props = msg:read('oa{sa{sv}}')
        if interfaces_and_props[interface_name] then
            log:notice('device obj added, path is %s', path)
            cb(path)
        end
    end)
end

function comm_fun.set_interface_del_signal(bus, sig_slot, path_pattern, interface_name, cb)
    local interface_del_sig = match_rule.signal(interface_del.name, interface_del.interface):with_path_namespace(
        path_pattern)
    sig_slot[#sig_slot + 1] = bus:match(interface_del_sig, function(msg)
        local path, _ = msg:read('oas')
        cb(path)
    end)
end

-- 按照下划线分割字符
function comm_fun.get_position_by_object_name(str)
    local result = {}
    for w in string.gmatch(str, '[^_]+') do
        table.insert(result, w)
    end
    return result[#result]
end

function comm_fun.get_device_name_by_object_name(str, class_name)
    local result = {}
    for word in string.gmatch(str, "[^_]+") do
        table.insert(result, word)
    end
    result[1] = class_name
    return table.concat(result, "_")
end

function comm_fun.get_prop(dev_obj, name)
    if not dev_obj or not name then
        return nil
    end
    
    local ok, rsp_code, val = pcall(function()
        return dev_obj:get_property(name)
    end)
    if not ok or rsp_code ~= 0 then
        log:error('[device_mgmt]get_prop failed, rsp_code=%s, val=%s', rsp_code, val)
        return nil
    else
        return val
    end
end

return comm_fun